#ifndef _SCHED_H
#define _SCHED_H

#define HZ 100

#define NR_TASKS 64
#define TASK_SIZE       0x04000000
#define LIBRARY_SIZE    0x00400000

#define CT_TO_SECS(x)   ((x) / HZ)
#define CT_TO_USECS(x)  (((x) % HZ) * 1000000/HZ)

#if (TASK_SIZE & 0x3fffff)
#error "TASK_SIZE must be multiple of 4M"
#endif

#if (((TASK_SIZE>>16)*NR_TASKS) != 0x10000)
#error "TASK_SIZE*NR_TASKS must be 4GB"
#endif

#define FIRST_TASK task[0]
#define LAST_TASK task[NR_TASKS-1]

#define LIBRARY_OFFSET (TASK_SIZE - LIBRARY_SIZE)

#include <linux/head.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <sys/param.h>
#include <signal.h>

#define TASK_RUNNING            0
#define TASK_INTERRUPTIBLE      1
#define TASK_UNINTERRUPTIBLE    2
#define TASK_ZOMBIE             3
#define TASK_STOPPED            4

#ifndef NULL
#define NULL ((void *) 0)
#endif

extern void schedule(void);
extern int copy_page_tables(unsigned long from, unsigned long to, long size);
extern int free_page_tables(unsigned long from, unsigned long size);

void trap_init();
extern void panic(const char * str);
extern int tty_write(unsigned minor,char * buf,int count);
void sched_init();
void add_timer(long jiffies, void (*fn)(void));
void sleep_on(struct task_struct ** p);
void wake_up(struct task_struct ** p);
extern int in_group_p(gid_t grp);
void test_a();
void test_b();

typedef int (*fn_ptr)();

struct tss_struct {
    long back_link;
    long esp0;
    long ss0;
    long esp1;
    long ss1;
    long esp2;
    long ss2;
    long cr3;
    long eip;
    long eflags;
    long eax, ecx, edx, ebx;
    long esp;
    long ebp;
    long esi;
    long edi;
    long es;
    long cs;
    long ss;
    long ds;
    long fs;
    long gs;
    long ldt;
    long trace_bitmap;
};

struct task_struct {
    long state;
    long counter;
    long priority;
    long signal;
    struct sigaction sigaction[32];
    long blocked;
    int exit_code;
    unsigned long start_code,end_code,end_data,brk,start_stack;
    long pid,pgrp,session,leader;
    int     groups[NGROUPS];
    struct task_struct      *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
    unsigned short uid,euid,suid;
    unsigned short gid,egid,sgid;
    unsigned long timeout,alarm;
    long utime,stime,cutime,cstime,start_time;

    unsigned int flags;

    /* file info */
    int tty;
    unsigned short umask;
    struct m_inode * pwd;
    struct m_inode * root;
    struct m_inode * executable;
    struct m_inode * library;
    unsigned long close_on_exec;
    struct file * filp[NR_OPEN];

    struct desc_struct ldt[3];
    struct tss_struct tss;
};

#define INIT_TASK \
{                   \
    0,              \
    15,             \
    15,             \
/* signals */   0,{{},},0, \
/* ec,brk... */ 0, 0,0,0,0,0, \
/* pid etc.. */ 0,0,0,0, \
/* suppl grps*/ {NOGROUP,}, \
    &init_task.task, 0, 0, 0,\
/* uid etc */   0,0,0,0,0,0, \
/* timeout */   0,0,0,0,0,0,0, \
/* flags */     0, \
    0,0022,NULL,NULL,NULL,NULL,0, \
    {NULL,}, \
    {               \
        {0, 0},     \
        {0x9f, 0xc0fa00},   \
        {0x9f, 0xc0f200},   \
    },              \
    {0, PAGE_SIZE + (long)&init_task, 0x10, 0, 0, 0, 0, (long)&pg_dir, \
        0, 0, 0, 0, 0, 0, 0, 0, \
        0, 0, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,   \
        _LDT(0), 0x80000000,    \
    },              \
}

extern struct task_struct *task[NR_TASKS];
extern struct task_struct *current;

extern unsigned long volatile jiffies;
extern unsigned long startup_time;

#define CURRENT_TIME (startup_time + jiffies / HZ)

/*
 * In linux is 4, because we add video selector,
 * so, it is 5 here.
 * */
#define FIRST_TSS_ENTRY 5
#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY + 1)
#define _TSS(n) ((((unsigned long)n) << 4) + (FIRST_TSS_ENTRY << 3))
#define _LDT(n) ((((unsigned long)n) << 4) + (FIRST_LDT_ENTRY << 3))
#define ltr(n) __asm__("ltr %%ax"::"a"(_TSS(n)))
#define lldt(n) __asm__("lldt %%ax"::"a"(_LDT(n)))

#define switch_to(n) {\
    struct {long a,b;} __tmp; \
    __asm__("cmpl %%ecx,current\n\t" \
            "je 1f\n\t" \
            "movw %%dx,%1\n\t" \
            "xchgl %%ecx,current\n\t" \
            "ljmp *%0\n\t" \
            "1:" \
            ::"m" (*&__tmp.a),"m" (*&__tmp.b), \
            "d" (_TSS(n)),"c" ((long) task[n])); \
}

#define _set_base(addr,base) \
__asm__("movw %%dx,%0\n\t" \
        "rorl $16,%%edx\n\t" \
        "movb %%dl,%1\n\t" \
        "movb %%dh,%2" \
        ::"m" (*((addr)+2)), \
        "m" (*((addr)+4)), \
        "m" (*((addr)+7)), \
        "d" (base) \
        :)

#define _set_limit(addr,limit) \
__asm__("movw %%dx,%0\n\t" \
        "rorl $16,%%edx\n\t" \
        "movb %1,%%dh\n\t" \
        "andb $0xf0,%%dh\n\t" \
        "orb %%dh,%%dl\n\t" \
        "movb %%dl,%1" \
        ::"m" (*(addr)), \
        "m" (*((addr)+6)), \
        "d" (limit) \
        :)

#define set_base(ldt,base) _set_base( ((char *)&(ldt)) , base )
#define set_limit(ldt,limit) _set_limit( ((char *)&(ldt)) , (limit-1)>>12 )

#define _get_base(addr) ({\
unsigned long __base; \
__asm__("movb %3,%%bh\n\t" \
    "movb %2,%%bl\n\t" \
    "shll $16,%%ebx\n\t" \
    "movw %1,%%bx" \
    :"=b" (__base) \
    :"m" (*((addr)+2)), \
    "m" (*((addr)+4)), \
    "m" (*((addr)+7))); \
__base;})

#define get_base(ldt) _get_base( ((char *)&(ldt)) )

#define get_limit(segment) ({ \
unsigned long __limit; \
__asm__("lsll %1,%0\n\tincl %0":"=r" (__limit):"r" (segment)); \
__limit;})

#endif
